#pragma once

#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include "Log.hpp"
#include "Common.hpp"

// 这个接口如果调用进程原来是进程组组长， 则会报错， 为了避免这
// 种情况， 我们通常的使用方法是先调用 fork 创建子进程， 父进程终止， 子进程继续
// 执行， 因为子进程会继承父进程的进程组 ID, 而进程 ID 则是新分配的， 就不会出现
// 错误的情况。

const std::string dev = "/dev/null";
// 将服务守护进程化的服务
void Daemon(int nochdir, int noclose)
{

    // 忽略IO，子进程退出相关的信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    // 2.父进程直接结束
    if (fork() > 0)
        exit(0);

    // 3.直接让父进程退出，到这里就是一个孤儿进程
    setsid(); // 成为一个独立的会话

    //为什么
    if(nochdir == 0)//如果设置为1的话，工作路径不发生改变
        chdir("/");//更改工作目录为根目录

    // 4.依旧可能与显示器，键盘，stdin，stdout，stderr关联
    // 守护进程，不从键盘输入，也不需要向显示器打印
    // 方法一:直接关0,1,2（不好）
    // 方法二:设备文件/dev/null,凡是写入该文件的内容，都被丢弃，凡是从该文件读取的内容都为空

    if(noclose == 0)//只有当noclose为0的时候，才会把三个文件描述符重定向
    {
        int fd = ::open(dev.c_str(), O_RDWR);
        if (fd < 0)
        {
            LOG(LogLevel::FATAL) << "open" << dev << " error";
            exit(OPEN_ERR);
        }
        else
        {
            //把三个文件描述符重定向
            dup2(fd,0);
            dup2(fd,1);
            dup2(fd,2);
            
            close(fd);
        }
    }
}
